package com.kookapi.service.impl;

import cn.hutool.json.JSONUtil;

import com.kookapi.business.domain.ApiDefinition;
import com.kookapi.business.domain.TestScenario;
import com.kookapi.business.domain.TestScenarioData;
import com.kookapi.business.domain.TestStep;
import com.kookapi.service.AllureReportService;
import io.qameta.allure.Allure;
import io.qameta.allure.model.Label;
import io.qameta.allure.model.TestResult;
import io.qameta.allure.model.TestResultContainer;
import io.restassured.response.Response;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Arrays;
import java.util.Map;

/**
 * @author bairimengchang
 * @version 1.0
 * @project java-test
 * @description 阿鲁报告业务接口实现类
 * @date 2025/10/2
 */
@Service
@Slf4j
public class AllureReportServiceImpl implements AllureReportService {

    // 测试结果保存目录，这里直接使用默认值即可
    @Value("${allure.results.directory:allure-results}")
    private String allureResultsDir;

    @Override
    public TestResultContainer startTestContainer(TestScenario testScenario) {
        // 创建测试套件（Suite）->对应系统中的业务场景
        // 创建测试套件的UUID ->在报告中指的是功能菜单下的二级菜单标题信息
        // 测试套件使用固定UUID（基于场景ID）
        String suiteUUID = "scenario-" + testScenario.getId().toString();
        // 创建测试套件的业务场景名字
        String suiteName = "业务场景: " + testScenario.getName();
        // 创建测试套件
        TestResultContainer testResultContainer = new TestResultContainer()
                .setUuid(suiteUUID)
                .setName(suiteName)
                .setDescription(testScenario.getDescription());
        // 生命周期开始
        Allure.getLifecycle().startTestContainer(suiteUUID, testResultContainer);

        return testResultContainer;
    }

    @Override
    public TestResult getTestResultInit(
            TestScenario testScenario,
            TestScenarioData scenarioData,
            TestResultContainer testResultContainer
    ) {
        // 测试场景名称
        String suiteName = testScenario.getName();

        // 创建测试用例（指的就是三级菜单标题信息）
        // 测试用例的UUID（使用固定的）
        String testCaseUUID = "testcase-" + scenarioData.getId().toString();

        TestResult testResult = new TestResult();
        testResult.setUuid(testCaseUUID);
        testResult.setName(scenarioData.getDataName());  // 用例名称用参数话数据组的名称（比如管理员操作/平台用户操作）
        testResult.setFullName(suiteName + "." + scenarioData.getDataName()); // 使用点号分隔
        testResult.setDescription(scenarioData.getDescription());
        testResult.setStart(System.currentTimeMillis());

        // 添加标签，便于分类筛选（指的是功能标签下的一级菜单）
        testResult.setLabels(Arrays.asList(
                new Label().setName("suite").setValue(suiteName),
                new Label().setName("epic").setValue("接口自动化测试"),
                new Label().setName("feature").setValue(suiteName),
                new Label().setName("story").setValue(scenarioData.getDataName())
        ));

        // 将测试用例关联到套件
        Allure.getLifecycle().scheduleTestCase(testResultContainer.getUuid(), testResult);
        Allure.getLifecycle().startTestCase(testCaseUUID);

        return testResult;
    }

    @Override
    public void recordHttpRequestResponse(
            TestStep testStep,
            ApiDefinition apiDef,
            Map<String, String> headersMap,
            String actualUrl,
            String actualBody,
            Response response
    ) {
        Allure.step(testStep.getName(), step -> {

            // 创建请求信息附件
            Allure.addAttachment(
                    "HTTP请求",
                    "text/plain",
                    String.format(
                            "URL: %s %s\nHeaders: %s\nBody: %s",
                            apiDef.getMethod(),
                            actualUrl,
                            headersMap,
                            actualBody)
            );

            // 创建响应信息附件
            Allure.addAttachment(
                    "HTTP响应",
                    "text/plain",
                    String.format(
                            "Status: %d\nHeaders: %s\nBody: %s",
                            response.getStatusCode(),
                            response.getHeaders(),
                            JSONUtil.formatJsonStr(response.getBody().asString())
                    )
            );
        });

    }

    @Override
    public boolean generateHtmlReport(Long taskId) {
        try {
            String reportDir = "allure-results-html/task-" + taskId;

            // TODO 这里需要完整命令行，执行allure generate命令
            String[] generateCommand = {
                    "D:\\D-DevEnv\\python\\allure-2.34.1\\bin\\allure.bat", "generate",
                    allureResultsDir,
                    "-o", reportDir,
                    "--clean"
            };
            log.info("执行命令: {}", String.join(" ", generateCommand));

            Process process = Runtime.getRuntime().exec(generateCommand);
            int exitCode = process.waitFor();

            if (exitCode == 0) {
                // 生成成功后，删除allureResultsDir目录内容
                deleteAllureResultsDir();
                // 返回报告访问路径(完整访问路径：http://localhost:8080/allure-reports/task-1/index.html)
                // 报告生成成功，返回true
                return true;
            } else {
                return false;
            }

        } catch (Exception e) {
            throw new RuntimeException("生成报告失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除allure测试结果目录
     */
    private void deleteAllureResultsDir() {
        try {
            File directory = new File(allureResultsDir);
            if (directory.exists() && directory.isDirectory()) {
                deleteDirectory(directory);
                log.info("删除成功: {}", allureResultsDir);
            }
        } catch (Exception e) {
            log.warn("删除目录失败: {}", e.getMessage());
        }
    }

    /**
     * 删除目录及其下的所有文件
     *
     * @param directory 目录
     */
    private void deleteDirectory(File directory) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    file.delete();
                }
            }
        }
        directory.delete();
    }

}
